package com.itxh.mybatis.test;

import com.itxh.mybatis.dao.UserDao;
import com.itxh.mybatis.dao.UserDaoImplWithJdbc3;
import com.itxh.mybatis.model.*;
import com.itxh.mybatis.po.User;
import org.apache.commons.dbcp.BasicDataSource;
import org.dom4j.*;
import org.dom4j.io.SAXReader;
import org.junit.Before;
import org.junit.Test;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;


public class UserDaoTestWithJdbc3 {

	private Configuration configuration;

	@Before
	public void before(){
		configuration = new Configuration();
		String location = "SqlMapConfig.xml";
		InputStream is = getResourceAsStream(location);
		Document document = createDocument(is);
		parseConfiguration(document.getRootElement());
	}

	private void parseConfiguration(Element rootElement) {
		parseEnvironments(rootElement.element("environments"));

		parseMappers(rootElement.element("mappers"));

	}

	private void parseMappers(Element mappers) {
		List<Element> mapperElements = mappers.elements();
		for (Element mapperElement : mapperElements) {
			String resource = mapperElement.attributeValue("resource");
			InputStream is = getResourceAsStream(resource);
			Document document = createDocument(is);
			parseMapper(document.getRootElement());
		}
	}

	private void parseMapper(Element mapperElement) {
		String namespace = mapperElement.attributeValue("namespace");
		if (namespace == null || namespace.equals("")){
			return;
		}
		List<Element> selectElements = mapperElement.elements("select");
		for (Element selectElement : selectElements) {
			parseStatement(namespace,selectElement);
		}
	}

	private void parseStatement(String namespace, Element selectElement) {
		String statementId = selectElement.attributeValue("id");
		if (statementId == null || statementId.equals("")){
			return;
		}
		statementId = namespace + "." + statementId;
		String parameterType = selectElement.attributeValue("parameterType");
		String resultType = selectElement.attributeValue("resultType");
		if (resultType == null || resultType.equals("")){
			return;
		}
		String statementType = selectElement.attributeValue("statementType");
		statementType = statementType == null || statementType.equals("")?"prepared":statementType;

		SqlSource sqlSource = createSqlSource(selectElement);

		MappedStatement mappedStatement = new MappedStatement(statementId,parameterType,resultType,statementType,sqlSource);
		configuration.addMappedStatement(statementId,mappedStatement);
	}

	private boolean isDynamic = false;
	private SqlSource createSqlSource(Element selectElement) {
		// 获取所有的SqlNode
		SqlNode mixedSqlNode = parseDynamicTags(selectElement);
		// 在获取SqlNode的过程中，会知道是否包含${}或者是否包含动态标签
		SqlSource sqlSource = null;
		// 通过包含${}或者动态标签，来分别创建SqlSource
		if (isDynamic){
			sqlSource = new DynamicSqlSource(mixedSqlNode);
		}else{
			sqlSource = new RawSqlSource(mixedSqlNode);
		}
		return sqlSource;
	}

	private SqlNode parseDynamicTags(Element selectElement) {
		List<SqlNode> sqlNodes = new ArrayList<>();

		int nodeCount = selectElement.nodeCount();
		SqlNode sqlNode = null;
		for (int i = 0; i < nodeCount; i++) {
			Node node = selectElement.node(i);
			if (node instanceof Text){
				String sqlText = node.getText().trim();
				if(sqlText == null || sqlText.equals("")){
					continue;
				}
				if (sqlText.indexOf("${") > -1){
					isDynamic = true;
					sqlNode = new TextSqlNode(sqlText);
					sqlNodes.add(sqlNode);
				}else{
					sqlNode = new StaticTextSqlNode(sqlText);
					sqlNodes.add(sqlNode);

				}
			}else if (node instanceof Element){
				isDynamic = true;

				Element element = (Element) node;
				String name = element.getName();
				if (name.equals("if")){
					String test = element.attributeValue("test");
					SqlNode mixedSqlNode = parseDynamicTags(element);
					sqlNode = new IfSqlNode(test,mixedSqlNode);
					sqlNodes.add(sqlNode);
				}//TODO
			}else{

			}
		}

		return new MixedSqlNode(sqlNodes);
	}

	private void parseEnvironments(Element environments) {
		String defaultEnvId = environments.attributeValue("default");
		if(defaultEnvId == null || defaultEnvId.equals("")){
			return ;
		}
		List<Element> elements = environments.elements();
		for (Element envElement : elements) {
			String envId = envElement.attributeValue("id");
			if(defaultEnvId.equals(envId)){
				parseDataSource(envElement.element("datasource"));
			}
		}
	}

	private void parseDataSource(Element datasource) {
		String type = datasource.attributeValue("type");
		if(type == null || type.equals("")){
			return ;
		}
		Properties properties = parseProperty(datasource);
		if (type.equals("DBCP")){
			BasicDataSource dataSource = new BasicDataSource();
			dataSource.setDriverClassName(properties.getProperty("driverClassName"));
			dataSource.setUrl(properties.getProperty("url"));
			dataSource.setUsername(properties.getProperty("username"));
			dataSource.setPassword(properties.getProperty("password"));

			configuration.setDataSource(dataSource);
		}
	}

	private Properties parseProperty(Element datasource) {
		List<Element> propertyElements = datasource.elements("property");

		Properties properties = new Properties();
		for (Element propertyElement : propertyElements) {
			String name = propertyElement.attributeValue("name");
			String value = propertyElement.attributeValue("value");

			properties.setProperty(name,value);
		}
		return properties;
	}

	private Document createDocument(InputStream is) {
		SAXReader saxReader = new SAXReader();
		try {
			return saxReader.read(is);
		} catch (DocumentException e) {
			e.printStackTrace();
		}
		return null;
	}

	private InputStream getResourceAsStream(String location) {
		return this.getClass().getClassLoader().getResourceAsStream(location);
	}






	@Test
	public void testQueryUserById() {
		UserDao userDao = new UserDaoImplWithJdbc3(configuration);
		User user = userDao.queryUserById(2);
		System.out.println(user);

	}

	@Test
	public void testQueryUsersByUsername() {
		UserDao userDao = new UserDaoImplWithJdbc3(configuration);

		List<User> users = userDao.queryUserByName("小妲己");
		System.out.println(users);
	}

}
